package system

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"soma-server/common"
	"soma-server/model/system"
	"soma-server/pkg/util"
	"soma-server/dto"
	"soma-server/vo"

	"github.com/gin-gonic/gin"
	"github.com/patrickmn/go-cache"
	"github.com/thoas/go-funk"
)

type IUserService interface {
	Login(user *system.User) (*dto.UserInfoDto, error)                           // 登录
	ChangePwd(username string, newPasswd string) error                           // 更新密码
	CreateUser(user *system.User) error                                          // 创建用户
	GetUserById(id uint) (system.User, error)                                    // 获取单个用户
	GetUserByIds(id []uint) ([]system.User, error)                               // 获取多个用户
	GetUsers(req *vo.UserListRequest) ([]*system.User, int64, error)             // 获取用户列表
	UpdateUser(user *system.User) error                                          // 更新用户
	BatchDeleteUserByIds(ids []uint) error                                       // 批量删除
	GetCurrentUser(c *gin.Context) (system.User, error)                          // 获取当前登录用户信息
	GetCurrentUserMinRoleSort(c *gin.Context) (uint, []uint, system.User, error) // 获取当前用户角色排序最小值、角色列表、以及当前用户信息
	GetUserMinRoleSortsByIds(ids []uint) ([]int, []uint, error)                  // 根据用户ID获取用户角色排序最小值
	ClearUserInfoCache()                                                         // 清理所有用户信息缓存
}

type UserService struct {
}

// 当前用户信息缓存，避免频繁获取数据库
var userInfoCache = cache.New(24*time.Hour, 48*time.Hour)

// UserService构造函数
func NewUserService() IUserService {
	return UserService{}
}

// 登录
func (ur UserService) Login(user *system.User) (*dto.UserInfoDto, error) {
	// 根据用户名获取用户(正常状态:用户状态正常)
	var (
		firstUser   system.User
		activeRoles []*system.Role
	)

	err := common.DB.
		Where("username = ?", user.Username).
		Preload("Roles").
		First(&firstUser).Error
	if err != nil {
		return nil, errors.New(firstUser.Username + "用户不存在")
	}

	// 判断用户的状态
	userStatus := firstUser.Status
	if userStatus != 1 {
		return nil, errors.New(firstUser.Username + "用户被禁用")
	}

	// 判断用户拥有的所有角色的状态,全部角色都被禁用则不能登录
	roles := firstUser.Roles
	isValidate := false
	for _, role := range roles {
		// 有一个正常状态的角色就可以登录
		if role.Status == 1 {
			activeRoles = append(activeRoles, role)
		}
		if len(activeRoles) > 0 {
			isValidate = true
		}
	}

	firstUser.Roles = activeRoles

	if !isValidate {
		return nil, errors.New(firstUser.Username + "用户角色被禁用")
	}

	// 校验密码
	err = util.ComparePasswd(firstUser.Password, user.Password)
	if err != nil {
		return dto.ToUserInfoDto(firstUser), errors.New(firstUser.Username + "密码错误")
	}
	return dto.ToUserInfoDto(firstUser), nil
}

// 获取当前登录用户信息
// 需要缓存，减少数据库访问
func (ur UserService) GetCurrentUser(c *gin.Context) (system.User, error) {
	var newUser system.User
	ctxUser, exist := c.Get("user")
	if !exist {
		return newUser, errors.New("用户未登录")
	}
	u, _ := ctxUser.(dto.UserInfoDto)

	// 先获取缓存
	cacheUser, found := userInfoCache.Get(u.Username)
	var user system.User
	var err error
	if found {
		user = cacheUser.(system.User)
		err = nil
	} else {
		// 缓存中没有就获取数据库
		user, err = ur.GetUserById(u.ID)
		// 获取成功就缓存
		if err != nil {
			userInfoCache.Delete(u.Username)
		} else {
			userInfoCache.Set(u.Username, user, cache.DefaultExpiration)
		}
	}
	return user, err
}

// 获取当前用户角色排序最小值、角色列表、以及当前用户信息
func (ur UserService) GetCurrentUserMinRoleSort(c *gin.Context) (uint, []uint, system.User, error) {
	// 获取当前用户
	ctxUser, err := ur.GetCurrentUser(c)
	if err != nil {
		return 999, nil, ctxUser, err
	}
	// 获取当前用户的所有角色
	currentRoles := ctxUser.Roles
	// 获取当前用户角色的排序，和前端传来的角色排序做比较
	var currentRoleSorts []int
	var currentRoleIds []uint
	for _, role := range currentRoles {
		currentRoleSorts = append(currentRoleSorts, int(role.Sort))
		currentRoleIds = append(currentRoleIds, role.ID)
	}
	// 当前用户角色排序最小值（最高等级角色）
	currentRoleSortMin := uint(funk.MinInt(currentRoleSorts))
	currentRoleIdList := funk.Uniq(currentRoleIds).([]uint)

	return currentRoleSortMin, currentRoleIdList, ctxUser, nil
}

// 获取单个用户
func (ur UserService) GetUserById(id uint) (system.User, error) {
	var user system.User
	err := common.DB.Where("id = ?", id).Preload("Roles").First(&user).Error
	return user, err
}

// 获取多个用户
func (ur UserService) GetUserByIds(id []uint) ([]system.User, error) {
	var user []system.User
	err := common.DB.Where("id in ?", id).Preload("Roles").Find(&user).Error
	return user, err
}

// 获取用户列表
func (ur UserService) GetUsers(req *vo.UserListRequest) ([]*system.User, int64, error) {
	var list []*system.User
	db := common.DB.Model(&system.User{}).Order("created_at DESC")

	username := strings.TrimSpace(req.Username)
	if username != "" {
		db = db.Where("username LIKE ?", fmt.Sprintf("%%%s%%", username))
	}
	nickname := strings.TrimSpace(req.Nickname)
	if nickname != "" {
		db = db.Where("nickname LIKE ?", fmt.Sprintf("%%%s%%", nickname))
	}
	mobile := strings.TrimSpace(req.Mobile)
	if mobile != "" {
		db = db.Where("mobile LIKE ?", fmt.Sprintf("%%%s%%", mobile))
	}
	status := req.Status
	if status != 3 {
		db = db.Where("status = ?", status)
	}
	// currentPage > 0 且 pageSize > 0 才分页
	//记录总条数
	var total int64
	err := db.Count(&total).Error
	if err != nil {
		return list, total, err
	}
	currentPage := int(req.CurrentPage)
	pageSize := int(req.PageSize)
	if currentPage > 0 && pageSize > 0 {
		err = db.Offset((currentPage-1)*pageSize).Limit(pageSize).Preload("Roles", "status NOT IN (?)", 2).Find(&list).Error
	} else {
		err = db.Preload("Roles", "status NOT IN (?)", 2).Find(&list).Error
	}
	return list, total, err
}

// 更新密码
func (ur UserService) ChangePwd(username string, hashNewPasswd string) error {
	err := common.DB.Model(&system.User{}).Where("username = ?", username).Update("password", hashNewPasswd).Error
	// 如果更新密码成功，则更新当前用户信息缓存
	// 先获取缓存
	cacheUser, found := userInfoCache.Get(username)
	if err == nil {
		if found {
			user := cacheUser.(system.User)
			user.Password = hashNewPasswd
			userInfoCache.Set(username, user, cache.DefaultExpiration)
		} else {
			// 没有缓存就获取用户信息缓存
			var user system.User
			common.DB.Where("username = ?", username).First(&user)
			userInfoCache.Set(username, user, cache.DefaultExpiration)
		}
	}

	return err
}

// 创建用户
func (ur UserService) CreateUser(user *system.User) error {
	err := common.DB.Create(user).Error
	return err
}

// 更新用户
func (ur UserService) UpdateUser(user *system.User) error {
	err := common.DB.Model(user).Updates(user).Error
	if err != nil {
		return err
	}
	err = common.DB.Model(user).Association("Roles").Replace(user.Roles)

	//err := common.DB.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user).Error

	// 如果更新成功就更新用户信息缓存
	if err == nil {
		userInfoCache.Set(user.Username, *user, cache.DefaultExpiration)
	}
	return err
}

// 批量删除
func (ur UserService) BatchDeleteUserByIds(ids []uint) error {
	// 用户和角色存在多对多关联关系
	var users []system.User
	for _, id := range ids {
		// 根据ID获取用户
		copyId := id
		user, err := ur.GetUserById(copyId)
		if err != nil {
			return fmt.Errorf("未获取到ID为%d的用户", copyId)
		}
		if user.Username == "admin" {
			return fmt.Errorf("不能删除 admin 用户")
		}
		users = append(users, user)
	}

	err := common.DB.Select("Roles").Unscoped().Delete(&users).Error
	// 删除用户成功，则删除用户信息缓存
	if err == nil {
		for _, user := range users {
			userInfoCache.Delete(user.Username)
		}
	}
	return err
}

// 根据用户ID获取用户角色排序最小值
func (ur UserService) GetUserMinRoleSortsByIds(ids []uint) ([]int, []uint, error) {
	// 根据用户ID获取用户信息
	var userList []system.User
	var roleIdsList []uint
	err := common.DB.Where("id IN (?)", ids).Preload("Roles").Find(&userList).Error
	if err != nil {
		return []int{}, nil, err
	}
	if len(userList) == 0 {
		return []int{}, nil, errors.New("未获取到任何用户信息")
	}

	var roleMinSortList []int
	for _, user := range userList {
		roles := user.Roles
		if len(roles) == 0 {
			continue
		}
		var roleSortList []int
		for _, role := range roles {
			roleSortList = append(roleSortList, int(role.Sort))
			roleIdsList = append(roleIdsList, role.ID)
		}

		roleMinSort := funk.MinInt(roleSortList)
		roleMinSortList = append(roleMinSortList, roleMinSort)
	}
	return roleMinSortList, roleIdsList, nil
}

// 清理所有用户信息缓存
func (ur UserService) ClearUserInfoCache() {
	userInfoCache.Flush()
}
